package goroutine

import (
	"context"
	"fmt"
	"runtime"
	"time"
)

//协成可能没有等到任何消息造成协成泄露。
func GoroutineLeak() (goroutineCount int) {
	defer func() {
		if e := recover(); e != nil {

		}
		goroutineCount = runtime.NumGoroutine()
		return
	}()
	ch := make(chan struct{}, 1)
	daemon := make(chan struct{}, 1)
	for i := 0; i < 100; i++ {
		go func(c chan struct{}) {
			a := time.Tick(5 * time.Second)
			for {
				select {
				case x := <-c:
					fmt.Println(x)
					time.Sleep(2 * time.Second)
				case r := <-a:
					fmt.Println(r)
					daemon <- struct{}{}
					return
				}
			}
		}(ch)
	}
	<-daemon
	goroutineCount = runtime.NumGoroutine()
	return
}

//主协成开启N个子协成后，主协成发生崩溃会造成子协成泄露。
//主协成可以传递ctx 从上向下关闭子协成防止泄露
func GoroutineLeakWithContext(waitTime time.Duration) (remainGoroutine int) {
	ctx := context.Background()
	ctxC, cancelFn := context.WithCancel(ctx)
	ctxT, _ := context.WithTimeout(ctxC, 5*time.Second)
	defer func() {
		if e := recover(); e != nil {
			fmt.Println("程序XX方法发生异常，", e)
			cancelFn()
			time.Sleep(waitTime * time.Second)
			fmt.Println("current goroutines:", runtime.NumGoroutine())
			remainGoroutine = runtime.NumGoroutine()
			return
		}
		//正常方法退出应该可以不用去执行取消方法
		fmt.Println("current goroutines:", runtime.NumGoroutine())
		remainGoroutine = runtime.NumGoroutine()
		return
	}()

	for i := 0; i < 101; i++ {
		go func(ctx context.Context) {
			c, _ := context.WithTimeout(ctx, 5*time.Second)
			for {
				select {
				case <-c.Done():
					return
				default:
					fmt.Println("我执行我de", time.Now())
					time.Sleep(6 * time.Second)
				}
			}
		}(ctxC)
	}
	<-ctxT.Done()
	panic("")
	fmt.Println(time.Now())
	return
}

func exec(ctx context.Context) {

}
